#!/usr/bin/env python
#
# Generic data filtering functions
#
# Author:   Mike Murphy <mamurph@cs.clemson.edu>
# Revision: 23 September 2009
#
#   Copyright 2009 Clemson University
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
# This material is based upon work supported under a National Science
# Foundation Graduate Research Fellowship. Any opinions, findings,
# conclusions or recommendations expressed in this publication are those of
# the author(s) and do not necessarily reflect the views of the National
# Science Foundation.

'''
Filtering functions for individual rows of input data, where each row is
a sequence (usually a list) of data items. These functions provide simple
intermediate processing capabilities.
'''

import time, calendar


def SEL_ALWAYS(row):
   '''
   Simple row selector that always selects a row.

   @param row: Row to select
   @type row: sequence

   @rtype: bool
   @return: True
   '''
   return True
#


def SEL_NEVER(row):
   '''
   Simple row selector that never selects a row

   @param row: Row to select
   @type row: sequence

   @rtype: bool
   @return: False
   '''
   return False
#


def select_row(row, row_filter):
   '''
   Selects rows based on filtering criteria established by an external
   function. The row filter may be a function or a tuple. If a function is
   used, the function will be invoked with a single argument: the row itself.
   When a tuple is specified, the function to be invoked will be the first
   item in the tuple. The first argument to the function will be the row,
   followed by the remaining tuple items as verbatim arguments.

   @param row: Row to test
   @type row: sequence
   @param row_filter: Row filter (function or tuple)

   @rtype: bool
   @return: True if the row has been accepted, False if it has been rejected
   '''
   sel_line = True
   if row_filter:
      for item in row_filter:
         if type(item) is tuple:
	    if not item[0](row, *item[1:]):
	       sel_line = False
	       break
	 else:
            if not item(row):
               sel_line = False
	       break
   return sel_line
#


def filter_row(row, col_filter):
   '''
   Filters the columns of a row, returning a new row in filter order. If the
   row does not match the filter, None is returned. In the event the filter
   is None, a shallow copy of the row is returned.

   The column filter must be a sequence (nominally a tuple). Each item in
   the sequence constitutes a filter to match. An integer item matches a
   column number in the row (yielding Exceptions in case of invalid numbers).
   If a tuple is used as an item in the outer sequence, then the first item
   of the inner tuple is invoked as a function, passing the row, followed by
   the remaining tuple items, as arguments. Finally, any non-int, non-tuple
   type is treated as a function invoked with a single argument: the row
   itself. Any such function must return the verbatim item to be included in
   the filtered row, or None if nothing should be inserted.

   @param row: Row to be filtered
   @type row: sequence
   @param col_filter: Filter sequence as described above

   @return: a newly filtered row as a list, or None if no filters match
   '''
   new_row = []
   if col_filter:
      for item in col_filter:
         if type(item) is int:
            new_row.append(row[item])
	 elif type(item) is tuple:
            new_item = item[0](row, *item[1:])
            if new_item is not None:
	       new_row.append(new_item)
	 else:
            new_item = item(row)
            if new_item is not None:
	       new_row.append(new_item)
   else:
      new_row = row[:]
   #
   if new_row == []:
      new_row = None
   return new_row
#


def data_filter(row, row_filter, col_filter):
   '''
   Applies a row selector and column filter to an input row, returning the
   filtered row (as a list) if selected, or None if either the row was not
   selected or the row did not match any filters.

   @param row: Input row
   @type row: sequence
   @param row_filter: Row selector (see L{select_row} method)
   @param col_filter: Column filter (see L{filter_row} method)

   @return: a filtered row (as a list) or None
   '''
   out_data = None
   if select_row(row, row_filter):
      out_data = filter_row(row, col_filter)
   return out_data
#


def timestamp_to_gmtime(stamp, fmt='%Y-%m-%d-%H:%M'):
   '''
   Converts a timestamp in a format recognizable by the date module into a
   floating-point value containing seconds since the epoch in UTC.

   @param stamp: Input timestamp
   @type stamp: str
   @param fmt: Format of timestamp, supported by L{calendar} module
   @type fmt: str

   @rtype: float
   @return: floating-point time representation (UTC seconds)
   '''
   tp = time.strptime(stamp, fmt)
   return calendar.timegm(tp)
#
